﻿#include <sstream>

#include <remoteobjecthost.h>
#include <remoteobjectnode.h>
#include <threadsafe.h>
#include <Timer.h>

#include "consolegamebase.h"

static const char GameInstanceName[] = "GameInstance";

struct ConsoleGameBase::Private
{
	RunMode m_runMode;
	Role m_role;

	//class Infos
	MetaInfo m_controllerType;
	MetaInfo m_gameStateType;
	MetaInfo m_gameInstanceType;
	std::unordered_map<std::string, MetaInfo> m_actorTypes;

	//RemoteConnection
	std::unique_ptr<RemoteObjectHost> host;
	std::unique_ptr<RemoteObjectNode> node;

	//ObjectPool
	ThreadSafe::ObjectRegistry<Actor> objs;

	//Time Object
	ThreadSafe::ObjectRegistry<Actor> timedObject;


	std::chrono::system_clock::time_point worldTime;
	std::chrono::system_clock::duration worldTimeDelta;
	std::chrono::system_clock::duration minWorldTimeDelta;

	PlayerContorller* m_currentController;
	GameState* m_gameState;
	std::unique_ptr<GameInstance> m_gameInstance;

	PlayerId m_currentPlayerId;

	unsigned int maxShareId;

	EventLoop m_loop;
};

ConsoleGameBase::ConsoleGameBase():_P(new Private)
{
	RegisterActor(_P->m_controllerType = PlayerContorller::staticMetaInfo());
	RegisterActor(_P->m_gameStateType = GameState::staticMetaInfo());
	RegisterActor(_P->m_gameInstanceType = GameInstance::staticMetaInfo());
	
	_P->m_currentController = NULL;
	_P->m_gameState = NULL;
	_P->m_gameInstance = NULL;

	_P->m_currentPlayerId = {};

	_P->maxShareId = 1;

	SetMaxFPS(60);//默认以60帧运行
}

ConsoleGameBase::~ConsoleGameBase()
{

}

void ConsoleGameBase::startAsSingle()
{
	_P->m_runMode = RM_Single;
	_P->m_role = Role(Role::Role_Client | Role::Role_Server);
	start();
}

void ConsoleGameBase::startAsServer(int port)
{
	_P->m_runMode = RM_Single;
	_P->m_role = Role::Role_Server;
	_P->host.reset(new RemoteObjectHost());
	_P->host->Listen(std::string("0.0.0.0:") + std::to_string(port));
	start();
}

void ConsoleGameBase::startAsClient(std::string ip, int port)
{
	_P->m_runMode = RM_Single;
	_P->m_role = Role::Role_Client;
	_P->node.reset(new RemoteObjectNode());
	_P->node->connectTo(ip + ":" + std::to_string(port));
	start();
}

void ConsoleGameBase::startAsListenServer(int port)
{
	_P->m_runMode = RM_Single;
	_P->m_role = Role(Role::Role_Client | Role::Role_Server);
	_P->host.reset(new RemoteObjectHost());
	_P->host->Listen(std::string("0.0.0.0:") + std::to_string(port));
	start();
}

void ConsoleGameBase::end()
{
	EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
		while (true)
		{
			auto objs = _P->objs.GetContent();

			if (objs.empty()) {
				break;
			}

			Actor* a = *objs.begin();

			delete a;
		}
		_P->m_loop.exit();
		}));
}

ConsoleGameBase::Role ConsoleGameBase::GetRole() const
{
	return _P->m_role;
}

Actor * ConsoleGameBase::GetActor(std::string Name)
{
	for (auto i : _P->objs.GetContent()) {
		if (i->GetName() == Name) {
			return i;
		}
	}

	return nullptr;
}

std::vector<Actor*> ConsoleGameBase::GetAllActor()
{
	auto v = _P->objs.GetContent();
	return { v.begin(),v.end() };
}

Actor * ConsoleGameBase::PublicSpawnActor(MetaInfo m)
{
	if (!(_P->m_role & Role::Role_Server)) {
		return NULL;
	}

	auto ret = CreateActor(m);
	RegisterActor(ret);

	_P->host->enableRemoting(ret, AllocShareName());

	return ret;
}

Actor * ConsoleGameBase::PrivateSpawnActor(MetaInfo m)
{
	auto ret = CreateActor(m);
	RegisterActor(ret);
	//TODO

	return ret;
}

std::chrono::system_clock::time_point ConsoleGameBase::GetWorldTime()
{
	return _P->worldTime;
}

std::chrono::system_clock::duration ConsoleGameBase::GetWorldTimeDelta()
{
	return _P->worldTimeDelta;
}

void ConsoleGameBase::SetMaxFPS(int fps)
{
	_P->minWorldTimeDelta = std::chrono::seconds(1) / fps;
}

int ConsoleGameBase::GetMaxFPS() const
{
	return std::chrono::seconds(1) / _P->minWorldTimeDelta;
}

void ConsoleGameBase::EnableFrameEvent(Actor * a)
{
	_P->timedObject.Register(a);
}

void ConsoleGameBase::DisableFrameEvent(Actor * a)
{
	_P->timedObject.UnRegister(a);
}

void ConsoleGameBase::OnGameBegin()
{
	if (_P->m_role & Role::Role_Server) {
		//服务器模式下GameInstance是自身创建的
		_P->m_gameInstance.reset(new GameInstance());
		if (_P->m_runMode != RM_Single) {
			_P->host->enableRemoting(_P->m_gameInstance.get(), GameInstanceName);
		}

		EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
			OnGameInstanceReady();
			}));
	}
	else if (_P->m_role == Role::Role_Client) {
		//客户机模式下GmaeInstance是外部获取的
		_P->m_gameInstance.reset(_P->m_gameInstanceType.MetaCast<GameInstance>(_P->node->Acquire(GameInstanceName, _P->m_gameInstanceType)));
		_P->m_gameInstance->Initialized.Bind([=]() {OnGameInstanceReady(); });
	}

	_P->m_gameInstance->PlayerAdded.Bind([=](PlayerId id) {
		OnPlayerAdded(id);
		}, this,GenericSignal::ConnectType::Queue);
}

void ConsoleGameBase::OnGameInstanceReady()
{
	if (_P->m_runMode != RM_PureServer) {
		//玩家登录
		_P->m_currentPlayerId = _P->m_gameInstance->Login();
	}

	_P->worldTime = std::chrono::system_clock::now();
	posttime();
}

void ConsoleGameBase::OnPlayReady()
{
	gameBegin();
}

void ConsoleGameBase::OnGameEnd()
{
}

void ConsoleGameBase::OnPlayerAdded(PlayerId id)
{
	if (GetRole() & Role_Server) {//服务器负责创建玩家控制器
		PlayerContorller* controller = dynamic_cast<PlayerContorller*>(PublicSpawnActor(_P->m_controllerType));
		controller->SetPlayerId(id);
	}
}

void ConsoleGameBase::OnActorSpawned(Actor * a)
{
	if (dynamic_cast<PlayerContorller*>(a)) {
		auto con = dynamic_cast<PlayerContorller*>(a);

		if (con->GetPlayerId() == _P->m_currentPlayerId) {
			_P->m_currentController = con;
			OnPlayReady();
		}

	}
}

void ConsoleGameBase::ReplicaSpawn(std::string shareName, std::string className)
{
	if (!_P->m_actorTypes.count(className)) {
		return;
	}

	MetaInfo info = _P->m_actorTypes[className];

	auto ret = dynamic_cast<Actor*>(_P->node->Acquire(shareName.c_str(), info));

	ret->Initialized.Bind([=]() {
		RegisterActor(ret);
		}, this);

}

std::string ConsoleGameBase::AllocShareName()
{
	std::string shareName;

	auto id = _P->maxShareId;

	_P->maxShareId++;

	shareName = "actor:" + std::to_string(id);

	return shareName;
}

PlayerContorller * ConsoleGameBase::GetCurrentController()
{
	return _P->m_currentController;
}

std::vector<PlayerContorller*> ConsoleGameBase::GetPlayerController()
{
	auto con = GetAllActor();

	std::vector<PlayerContorller*> ret;

	for (auto i : con) {
		auto p = dynamic_cast<PlayerContorller*>(i);
		if (p) {
			ret.push_back(p);
		}
	}

	return ret;
}

void ConsoleGameBase::SetControllerType(MetaInfo meta)
{
	_P->m_controllerType = meta;
}

void ConsoleGameBase::SetGameStateType(MetaInfo meta)
{
	_P->m_gameStateType = meta;
}

void ConsoleGameBase::RegisterActor(MetaInfo meta)
{
	if (meta.IsValid()) {
		_P->m_actorTypes[meta.ClassName()] = meta;
	}
}

void ConsoleGameBase::SetGameInstanceType(MetaInfo meta)
{
	_P->m_gameInstanceType = meta;
}

void ConsoleGameBase::start()
{
	if (_P->node) {
		_P->node->newObject.Bind([=](std::string shareName,std::string className) {
			ReplicaSpawn(shareName, className);
			});
	}

	EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
		OnGameBegin();
		}));

	_P->m_loop.exec();
}

Actor * ConsoleGameBase::CreateActor(MetaInfo m)
{
	void *v = m.Create();
	Actor* ret = m.MetaCast<Actor>(v);

	return ret;
}

void ConsoleGameBase::RegisterActor(Actor * ret)
{
	_P->objs.Register(ret);

	EventLoop::PostEvent(ret, new ThreadSafe::InvokeFunctionEvent([=]() {
		ret->BeginPlay();
		}));

	EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
		OnActorSpawned(ret);
		}));
}

void ConsoleGameBase::posttime()
{
	auto now = std::chrono::system_clock::now();
	auto next = _P->worldTime + _P->minWorldTimeDelta;

	if (now < next) {
		Timer::SingleShot(next, [=]() {
			timeout();
			},this);
	}
	else {
		EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
			timeout();
			}));
	}
}

void ConsoleGameBase::timeout()
{
	auto now = std::chrono::system_clock::now();
	_P->worldTimeDelta = now - _P->worldTime;
	_P->worldTime = now;

	EventLoop::PostEvent(this, new ThreadSafe::InvokeFunctionEvent([=]() {
		doFrameWork();
		}));
}

void ConsoleGameBase::doFrameWork()
{
	for (auto i : _P->timedObject.GetContent()) {
		i->Frame(_P->worldTimeDelta);
	}
	posttime();
}
